#include <pthread.h>
#include <functional>
#include <iostream>
#include <mutex>
#include <unistd.h>

#define RETURN_FALSE(msg)              \
    do                                 \
    {                                  \
        std::cout << msg << std::endl; \
        return false;                  \
    } while (0)

// 基于线程库函数的调用封装
namespace ThreadModule
{
    std::uint32_t cnt = 0;
    static const int SUCCESS_RUN = 1;
    using func_t = std::function<void *(void *)>;

    enum class Status
    {
        INIT,
        RUNNING,
        FINISHED // 结束，已回收
    };

    inline std::string STS(Status s)
    {
        switch (s)
        {
        case Status::INIT:
            return "INIT";
            break;
        case Status::RUNNING:
            return "RUNNING";
            break;
        case Status::FINISHED:
            return "FINISHED";
            break;
        default:
            return "DEFUALT";
            break;
        };
    }

    class Thread
    {
    private:
        // static静态成员函数作为类本身没有this指针
        static void *run(void *obj)
        {
            Thread *self = static_cast<Thread *>(obj);
            pthread_setname_np(pthread_self(), self->_name.c_str());

            // 在线程运行结束前调度,不会重复调度
            if (!self->_joined)
            {
                if (pthread_detach(pthread_self()))
                {
                    std::cout << "failed to run:failed to detach" << std::endl;
                    return nullptr;
                }
                {
                    std::lock_guard<std::mutex> lock(self->_mtx);
                    self->_status = Status::FINISHED;
                }
            }
            {
                std::lock_guard<std::mutex> lock(self->_mtx);
                self->_status = Status::RUNNING;
            }
            self->_func(self->_arg);

            return (void *)SUCCESS_RUN;
        }

    public:
        Thread(func_t func, void *arg) : _status(Status::INIT),
                                         _func(func),
                                         _arg(arg),
                                         _joined(false)
        {
            _name = "Thread-" + std::to_string(cnt++);
        }

        Thread(const Thread &) = delete;
        Thread &operator=(const Thread &) = delete;

        void EnableJoined()
        {
            std::lock_guard<std::mutex> lock(_mtx);
            if (_status == Status::INIT)
                _joined = true;
        }

        void EnableDetach()
        {
            std::lock_guard<std::mutex> lock(_mtx);
            if (_status == Status::INIT)
                _joined = false;
        }

        bool start()
        {
            std::lock_guard<std::mutex> lock(_mtx);
            if (_status == Status::FINISHED)
                RETURN_FALSE("failed to start:status is finished");

            if (_status == Status::RUNNING)
                return true;

            if (pthread_create(&_id, nullptr, run, this))
                RETURN_FALSE("failed to start:failed to create a pthread");

            return true;
        }

        bool join()
        {
            if (_joined)
            {
                std::unique_lock<std::mutex> lock(_mtx);
                if (_status == Status::INIT)
                {
                    lock.unlock();
                    // 避免抢锁等待run执行func更换_status
                    usleep(1);
                    lock.lock();
                }

                if (_status == Status::RUNNING)
                {
                    lock.unlock();
                    if (pthread_join(_id, nullptr))
                        RETURN_FALSE("failed to join:pthread_join");
                    lock.lock();
                    _status = Status::FINISHED;
                    return true;
                }
            }
            std::cout << "_joined:" << _joined << " _status:" << STS(_status) << std::endl;
            RETURN_FALSE("failed to join:other");
        }

    private:
        std::string _name;
        pthread_t _id;
        Status _status;
        func_t _func;
        void *_arg;
        bool _joined;
        std::mutex _mtx;
    };
}